package com.edgar.download;

import android.content.Context;
import android.content.Intent;
import android.text.TextUtils;

import com.edgar.download.database.DownloadHelp;
import com.edgar.baselibrary.utils.ThreadUtils;

import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;

/**
 * Created by dell on 2015/9/23.
 * download manager
 */
public class DownloadManager {

    private Context mContext;

    private volatile boolean mInit = false;
    private static DownloadManager sInstance;

    //define download all queue.
    /** We need a queue to save the object being downloaded. **/
    private LinkedList<Download> mDownloadList = new LinkedList<Download>();
    /** We need a queue to save the object being pause.**/
    private LinkedList<Download> mDownloadAllList = new LinkedList<Download>();

    /** All observer list **/
    private Map<String,List<IDownloadListener>> mNotifyList = new HashMap<String,List<IDownloadListener>>();
    private Executor mExecutor = Executors.newSingleThreadExecutor();
    private DownloadHelp mDownloadHelp;

    private DownloadManager(Context context){
        this.mContext = context.getApplicationContext();
        mDownloadHelp = new DownloadHelp(mContext);
        initAllDownloadTaskForDb();
    }

    public static void init(Context context){
        sInstance = new DownloadManager(context);
    }

    public static DownloadManager getInstance(){
        if(sInstance == null){
            throw new NullPointerException("sInstance is null,pleas init downloadmanager.");
        }
        return sInstance;
    }

    /**
     * scan local database all download task,get operation maybe wait.
     */
    private void initAllDownloadTaskForDb(){
        execute(new Runnable() {
            @Override
            public void run() {
                synchronized (DownloadManager.this) {
                    mDownloadAllList.addAll(mDownloadHelp.queryAllDownload());
                    mInit = true;
                    DownloadManager.this.notifyAll();
                }
            }
        });
    }

    /**
     * 添加下载到到队列中
     * @param download 下载对象
     */
    public synchronized void addDownload(Download download){
        maybeWaitInit();
        //check Url already exists in download list.
        if(!mDownloadList.contains(download)){
            boolean found = false;
            boolean startService = false;
            //没有在下载队列中,在全部队列中查找.
            for (Download existsDownload : mDownloadAllList){
                if(existsDownload.getDownloadUrl().equals(download.getDownloadUrl())){
                    found = true;
                    int status = existsDownload.getStatus();
                    boolean noWaitOrStart = status != Constant.WAIT ||
                            status != Constant.START;
                    if(status != Constant.SUCCESS && noWaitOrStart){
                        existsDownload.setStatus(Constant.WAIT);
                        mDownloadList.addLast(existsDownload);
                        startService = true;
                    }
                    break;
                }
            }
            if(!found){
                download.setDownloadTime(System.currentTimeMillis());
                long id = mDownloadHelp.insertDownloadToDb(download.toContentValue());
                download.setId(id);
                download.setStatus(Constant.WAIT);
                mDownloadAllList.add(download);
                mDownloadList.addLast(download);
                startService = true;
            }
            if(startService){
                startDownloadService();
            }
        }
    }

    private void startDownloadService(){
        Intent intent = new Intent(mContext,DownloadService.class);
        mContext.startService(intent);
    }

    private void stopDownloadService(){
        Intent intent = new Intent(mContext,DownloadService.class);
        mContext.stopService(intent);
    }

    /**
     * 删除全部下载,该方法会删除所有正在下载的对象
     */
    public void deleteAllDownload(final Runnable runnable){
        mExecutor.execute(new Runnable() {
            @Override
            public void run() {
                synchronized (DownloadManager.this) {
                    int N = mDownloadAllList.size();
                    for (int i = 0; i < N; i++) {
                        Download download = mDownloadAllList.remove(i);
                        download.setStatus(Constant.PAUSE);
                        File file = new File(download.getFilePath());
                        if (file.exists()) {
                            file.delete();
                        }
                        N--;
                    }
                    mDownloadHelp.clearDownloadDb();
                    mDownloadList.clear();
                    ThreadUtils.runMainThread(createStopRunnable(runnable));
                }
            }
        });
    }

    /**
     * 删除下载
     * @param url
     */
    public void deleteDownloadWithUrl(final String url){
        final Download download = findDownloadByUrl(url);
        execute(new Runnable() {
            @Override
            public void run() {
                synchronized (DownloadManager.this) {
                    if (download != null) {
                        download.setCancel(true);
                        mDownloadHelp.deleteDownloadWithId(download.getId());
                        mDownloadList.remove(download);
                        mDownloadAllList.remove(download);
                    }
                }
                notifyCancel(url);
            }
        });
    }

    public void registerDownloadListener(String url, IDownloadListener downloadListener){
        if(downloadListener == null){
            return;
        }
        synchronized (mNotifyList){
            List<IDownloadListener> downloadListeners = mNotifyList.get(url);
            if(downloadListeners == null){
                downloadListeners = new ArrayList<IDownloadListener>();
                mNotifyList.put(url,downloadListeners);
            }
            if(!downloadListeners.contains(downloadListener)){
                downloadListeners.add(downloadListener);
            }
        }
    }

    public void unRegisterDownloadListener(String url, IDownloadListener downloadListener){
        if(downloadListener == null){
            return;
        }
        synchronized (mNotifyList){
            List<IDownloadListener> downloadListeners = null;
            if(!TextUtils.isEmpty(url)){
                downloadListeners = mNotifyList.get(url);
                if(downloadListeners != null) {
                    downloadListeners.remove(downloadListener);
                }
            } else {
                for(Map.Entry<String,List<IDownloadListener>> entry : mNotifyList.entrySet()){
                    downloadListeners = entry.getValue();
                    if(downloadListeners != null) downloadListeners.remove(downloadListener);
                }
            }
        }
    }

    void notifyPause(final String url){
        notifyMonitor(url, new IFoundListener() {
            @Override
            public void onFound(IDownloadListener listener) {
                listener.onPause(url);
            }
        });
    }

    public void onStartDownload(Download download){
        download.setStatus(Constant.START);
        mDownloadHelp.updateDownloadWithId(download.toUpdateContentValue(), download.getId());
        notifyStart(download.getDownloadUrl());
    }

    public void onDownloadFailure(Download download){
        download.setStatus(Constant.FAIL);
        mDownloadHelp.updateDownloadWithId(download.toUpdateContentValue(), download.getId());
        notifyFailure(download.getDownloadUrl(),download.getErrorStatusCode());
    }

    public void onDownloadComplete(Download download){
        download.setStatus(Constant.SUCCESS);
        mDownloadHelp.updateDownloadWithId(download.toUpdateContentValue(), download.getId());
        notifyComplete(download.getDownloadUrl());
    }

    public void notifyComplete(final String url){
        notifyMonitor(url, new IFoundListener() {
            @Override
            public void onFound(IDownloadListener listener) {
                listener.onComplete(url);
            }
        });
    }

    private void notifyFailure(final String url, final int failStatusCode){
        notifyMonitor(url, new IFoundListener() {
            @Override
            public void onFound(IDownloadListener listener) {
                listener.onFailure(url,failStatusCode);
            }
        });
    }

    private void notifyStart(final String url){
        notifyMonitor(url, new IFoundListener() {
            @Override
            public void onFound(IDownloadListener listener) {
                listener.onStart(url);
            }
        });
    }

    void notifyCancel(final String url){
        notifyMonitor(url, new IFoundListener() {
            @Override
            public void onFound(IDownloadListener listener) {
                listener.onCancel(url);
            }
        });
    }

    /** download task calling **/
    void updateDownloadToDatabase(Download download){
        short status = download.getStatus();
        mDownloadHelp.updateDownloadWithId(download.toUpdateContentValue(),download.getId());
        notifyUpdateProgress(download.getDownloadUrl(), status, download.getProgress());
    }

    void notifyUpdateProgress(final String url,final int status,final int progress){
        notifyMonitor(url, new IFoundListener() {
            @Override
            public void onFound(IDownloadListener listener) {
                listener.onDownload(url, status, progress);
            }
        });
    }

    private void notifyMonitor(String url,final IFoundListener foundListener){
        synchronized (mNotifyList){
            List<IDownloadListener> downloadListeners = findDownloadListenerByUrl(url);
            if(downloadListeners != null){
                for (final IDownloadListener downloadListener : downloadListeners){
                    if(downloadListener != null){
                        ThreadUtils.runMainThread(new Runnable() {
                            @Override
                            public void run() {
                                if(foundListener != null){
                                    foundListener.onFound(downloadListener);
                                }
                            }
                        });
                    }
                }
            }
        }
    }

    private List<IDownloadListener> findDownloadListenerByUrl(String url){
        List<IDownloadListener> downloadListeners = mNotifyList.get(url);
        return downloadListeners;
    }

    public synchronized Download pollNextTask(){
        if(mDownloadList.isEmpty()) return null;
        return mDownloadList.removeFirst();
    }

    /**
     * get all download task.
     * @return
     */
    public synchronized List<Download> getAllDownloadTasks(){
        maybeWaitInit();
        return mDownloadAllList;
    }

    /** Need wait download manager init.**/
    private void maybeWaitInit(){
        try {
            while (!mInit){
                wait();
            }
        } catch (InterruptedException e) {
            //
        }
    }

    public synchronized Download findDownloadByUrl(String url){
        for (Download download : mDownloadAllList){
            if(download.getDownloadUrl().equals(url)){
                return download;
            }
        }
        return null;
    }

    /**
     * 暂停某个url的下载
     * @param download
     */
    public void pauseDownloadWithDownload(final Download download){
        if(download != null){
            download.setStatus(Constant.PAUSE);
            mDownloadHelp.updateDownloadWithId(download.toUpdateContentValue(), download.getId());
            notifyPause(download.getDownloadUrl());
        }
    }

    /**
     * 暂停全部下载,正在下载的对象会被添加到pause队列中.
     */
    public void pauseAllDownload(final Runnable runnable){
        execute(new Runnable() {
            @Override
            public void run() {
                synchronized (DownloadManager.this) {
                    int N = mDownloadList.size();
                    for (int i = 0; i < N; i++) {
                        Download download = mDownloadList.get(i);
                        download.setStatus(Constant.PAUSE);
                        mDownloadHelp.updateDownloadWithId(download.toUpdateContentValue(), download.getId());
                        N--;
                    }
                }
                ThreadUtils.runMainThread(createStopRunnable(runnable));
            }
        });
    }

    private void execute(Runnable runnable){
        mExecutor.execute(runnable);
    }

    private Runnable createStopRunnable(final Runnable runnable){
        return new Runnable() {
            @Override
            public void run() {
               if(runnable != null){
                   runnable.run();
               }
                stopDownloadService();
            }
        };
    }

    private interface IFoundListener{
        void onFound(IDownloadListener listener);
    }
}